# coding=utf-8
from ToolsConfig import *

CreatedTools = {}


def RegisterSystemFastly(api, cls):
    """
    快速注册系统
    :param api:serverApi or clientApi
    :param cls:系统类
    :return:
    """
    api.RegisterSystem(
        MOD_NAME, cls.__name__,
        '{}.{}'.format(cls.__module__, cls.__name__)
    )


class EventFilter(object):
    def __init__(self, func, systemInstance, *args):
        self.fiterArgs = args
        self.systemFuncName = func.__name__
        self.systemInstance = systemInstance

    def Filter(self, *args):
        key = True
        for _ in self.fiterArgs:
            key = key and _(*args)
            if not key: break
        return getattr(self.systemInstance, self.systemFuncName)(*args) if key else None


class SystemTools(object):
    """
    系统工具箱,以不同类进行分辨
    """

    def __init__(self):
        self.bindingInstance = None
        self.systemListenEvent = set()
        self.filterEvent = set()
        self.filter = list()
        self.lastListened = set()

    @classmethod
    def GetTools(cls, tag):
        """
        创建系统工具
        :param tag:工具标签
        :return:SystemTools
        """
        tag = tag + MOD_NAME + MOD_VERSION
        instance = CreatedTools.get(tag)
        if instance is None:
            instance = cls()
            CreatedTools[tag] = instance
        else:
            CreatedTools[tag].Relisten()
        return instance

    def GetBlueFoxServerListener(self):
        """
        获取BlueFoxServer系统的监听器
        """
        return self.ListenForEvent(namespace=MOD_NAME, systemName='BlueFoxServerSystem')

    def GetBlueFoxClientListener(self):
        """
        获取BlueFoxClient系统的监听器
        """
        return self.ListenForEvent(namespace=MOD_NAME, systemName='BlueFoxClientSystem')

    def ListenForEvent(self, eventName=None, namespace=MC_SYSTEM_NAMESPACE, systemName=MC_SYSTEM_NAME, priority=0):
        """
        监听事件,binding的服务端可监听服务端事件,binding的客户端则可监听客户端事件
        :param eventName: 可选:事件名 or 不传入参数.若不传入参数,则将函数名称视为事件名称
        :param namespace:如果需要监听其他系统内的事件,需要传入系统的namespace
        :param systemName:如果需要监听其他系统内的事件,需要传入系统的systemName
        :param priority:执行优先级
        :return:
        """

        def Listen(func):
            EventName = (
                namespace, systemName, eventName if eventName else func.__name__,
                self.bindingInstance if self.bindingInstance else 0, func, priority
            )
            self.systemListenEvent.add(EventName)
            # 热更新重监听
            self.__listen(EventName)
            return func

        return Listen

    def __listen(self, eventName):
        if self.bindingInstance:
            self.bindingInstance.ListenForEvent(*eventName)
            self.lastListened.add(eventName)

    def FilterListener(self, eventName, namespace=MC_SYSTEM_NAMESPACE, systemName=MC_SYSTEM_NAME, priority=0):
        """
        滤过监听器, 通常用于优化代码可读性
        """

        def Filter(*args):
            # type:(Callable) -> Callable
            def Listener(func):
                _filter = EventFilter(func, self.bindingInstance, *args)
                EventName = (namespace, systemName, eventName, _filter, _filter.Filter, priority)
                self.filterEvent.add(EventName)
                self.__listen(EventName)
                return func

            return Listener

        return Filter

    def Binding(self, systemInstance):
        # type:(Union[serverBaseSystem,clientBaseSystem]) -> None
        """
        绑定系统类
        :param systemInstance:系统实例
        :return:
        """
        self.bindingInstance = systemInstance
        self.FirstListen()

    def Relisten(self):
        # 重监听之前需要取消监听监听过的事件
        for args in self.lastListened:
            self.bindingInstance.UnListenForEvent(*args)
        self.lastListened = set()
        self.filterEvent = set()

    def FirstListen(self):
        # 初次监听
        for args in self.systemListenEvent:
            args = list(args)
            args[3] = self.bindingInstance
            if hasattr(self.bindingInstance, args[4].__name__):
                self.bindingInstance.ListenForEvent(*args)
                self.lastListened.add(tuple(args))
        for args in self.filterEvent:
            args = list(args)
            args[3].systemInstance = self.bindingInstance
            self.bindingInstance.ListenForEvent(*args)
            self.lastListened.add(tuple(args))
